<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Color Picker</title>
  <link rel="stylesheet" href="assets/css/base.css">
  <script src="../build/react.js"></script>
  <script src="../build/react-dom.js"></script>
  <script src="../build/browser.min.js"></script>
  <script src="../build/jquery.min.js"></script>
</head>
<body>
<div class="container">

</div>
<script type="text/babel">
  function hslToRgb(h, s, l){//0-1的小数
    var r, g, b;
    if(s == 0){
      r = g = b = l; // achromatic
    }else{
      var hue2rgb = function hue2rgb(p, q, t){
        if(t < 0) t += 1;
        if(t > 1) t -= 1;
        if(t < 1/6) return p + (q - p) * 6 * t;
        if(t < 1/2) return q;
        if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
        return p;
      }

      var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
      var p = 2 * l - q;
      r = hue2rgb(p, q, h + 1/3);
      g = hue2rgb(p, q, h);
      b = hue2rgb(p, q, h - 1/3);
    }

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
  }
  function rgbToHsl(r, g, b){
    r /= 255, g /= 255, b /= 255;
    var max = Math.max(r, g, b), min = Math.min(r, g, b);
    var h, s, l = (max + min) / 2;

    if(max == min){
      h = s = 0; // achromatic
    }else{
      var d = max - min;
      s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
      switch(max){
        case r: h = (g - b) / d + (g < b ? 6 : 0); break;
        case g: h = (b - r) / d + 2; break;
        case b: h = (r - g) / d + 4; break;
      }
      h /= 6;
    }
    h = parseInt(h*360);
    s = parseInt(s*100)+"%";
    l = parseInt(l*100)+"%";

    return [h, s, l];
  }
  var renderBlockColor = function(rgba){//canvas渲染像素点
    var canvas = $('.color-block')[0];
    var context = canvas.getContext('2d');
    var width = 360;
    var height = 180;
    var imgData = context.createImageData(width,height);
    for (var i=0;i<imgData.data.length;i=i+4)
    {
      var x = (i%(360*4))/(360*4);//0-359
      var y = parseInt(i/(360*4))/179;//0-179
      imgData.data[i+0]=(255*(1-x)+rgba[0]*x)*(1-y);
      imgData.data[i+1]=(255*(1-x)+rgba[1]*x)*(1-y);
      imgData.data[i+2]=(255*(1-x)+rgba[2]*x)*(1-y);
      imgData.data[i+3]=rgba[3];
    }
    context.putImageData(imgData,0,0);
  };

  var ColorWrap = React.createClass({//取色器类
    getInitialState: function() {
      return {
        rgba:[0,255,255,255],
        value: 768,
        alpha: 255,
        pickRgb:[0,255,255],
        x: 359,
        y: 0,
        hex:'#0ff',
        hsl:[180,'100%','50%']
      };
    },
    handleValueChange: function(event){//滑动rgb颜色条
      this.setState({value: event.target.value});
      var rangeVal =this.state.value;
      var alpha = this.state.alpha;
      var color = '';
      var rgb = [];
      if(rangeVal<=255) {
        color = 'rgb(255,0,'+rangeVal+')';
        rgb = [255,0,rangeVal];
      }else if(rangeVal<256*2&&rangeVal>=256){
        color = 'rgb('+(2*255-rangeVal)+',0,255)';
        rgb = [2*255-rangeVal,0,255];
      }else if(rangeVal<256*3&&rangeVal>=256*2){
        color = 'rgb(0,'+(rangeVal-255*2)+',255)';
        rgb = [0,rangeVal-255*2,255];
      }else if(rangeVal<256*4&&rangeVal>=256*3){
        color = 'rgb(0,255,'+(255*4-rangeVal)+')';
        rgb = [0,255,255*4-rangeVal];
      }else if(rangeVal<256*5&&rangeVal>=256*4){
        color = 'rgb('+(rangeVal-255*4)+',255,0)';
        rgb = [rangeVal-255*4,255,0];
      }else if(rangeVal<256*6&&rangeVal>=256*5){
        color = 'rgb(255,'+(255*6-rangeVal)+',0)';
        rgb = [255,255*6-rangeVal,0];
      }
      var rgba = rgb.concat([alpha]);
      renderBlockColor(rgba);
      var x = this.state.x;
      var y = this.state.y;
      var pickRgb =[];
      pickRgb[0] = parseInt((255*(1-x/359)+rgba[0]*x/359)*(1-y/179));
      pickRgb[1] = parseInt((255*(1-x/359)+rgba[1]*x/359)*(1-y/179));
      pickRgb[2] = parseInt((255*(1-x/359)+rgba[2]*x/359)*(1-y/179));
      this.setState({rgba:rgba,pickRgb:pickRgb,hex:'#'+pickRgb.map(function(cv){
        if(cv>=16){
          return cv.toString(16);
        }else{
          return '0'+cv.toString(16);
        }
      }).join(''),hsl:rgbToHsl(pickRgb[0],pickRgb[1],pickRgb[2])});


    },
    handleAlphaChange: function(event){////滑动alpha条
      this.setState({alpha: event.target.value});
      var alpha =this.state.alpha;
      var rgba = this.state.rgba;
      rgba[3] = alpha;
      renderBlockColor(rgba);
      this.setState({rgba:rgba});
      if(alpha<255&&!$('.hex').hasClass('hide')){
        $('.hex').addClass('hide').next().removeClass('hide');
      }
    },
    handlePickerWrapClick: function (event) {//切换颜色单位制式
      var e = event || window.event;
      var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
      var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
      var x = e.pageX || e.clientX + scrollX;
      var y = e.pageY || e.clientY + scrollY;
      var canvasX = $('.color-picker-wrap').offset().left;
      var canvasY = $('.color-picker-wrap').offset().top;
      x = x-canvasX;
      y = y-canvasY;
      $('.color-picker').css({left:x-6,top:y-6});

      var rgba = this.state.rgba;
      var pickRgb = [];
      pickRgb[0] = parseInt((255*(1-x/359)+rgba[0]*x/359)*(1-y/179));
      pickRgb[1] = parseInt((255*(1-x/359)+rgba[1]*x/359)*(1-y/179));
      pickRgb[2] = parseInt((255*(1-x/359)+rgba[2]*x/359)*(1-y/179));
      var color = 'rgba('+pickRgb[0]+ ','+pickRgb[1]+ ','+pickRgb[2]+','+rgba[3]+')';
      $('.circle').css('background',color);
      this.setState({x:x,y:y,pickRgb:pickRgb,hex:'#'+pickRgb.map(function(cv){
        if(cv>=16){
          return cv.toString(16);
        }else{
          return '0'+cv.toString(16);
        }
      }).join(''),hsl:rgbToHsl(pickRgb[0],pickRgb[1],pickRgb[2])});
    },
    handleSwitch: function(){
      var index = $('.color-show li').index($('.color-show li:not(.hide)'));
      index===2?index=0:index++;
      if(this.state.alpha!==255){
        index===0?index=1:null;
      }
      console.log(index);
      $('.color-show li').eq(index).removeClass('hide').siblings('li').addClass('hide');
    },
    handleHexChange: function(event){
      var rgb = [];
      var inputVal = $.trim(event.target.value);
      this.setState({hex:inputVal});
      var pattern1 = /^#[0-9a-fA-F]{3}$/;
      var pattern2 = /^#[0-9a-fA-F]{6}$/;
      if(pattern1.test(inputVal)){
        rgb[0] = parseInt(inputVal.substr(1,1),16)*17;
        rgb[1] = parseInt(inputVal.substr(2,1),16)*17;
        rgb[2] = parseInt(inputVal.substr(3,1),16)*17;
      }else if(pattern2.test(inputVal)){
        rgb[0] = parseInt(inputVal.substr(1,2),16);
        rgb[1] = parseInt(inputVal.substr(3,2),16);
        rgb[2] = parseInt(inputVal.substr(5,2),16);
      }else{
        return false
      }
      var max = Math.max.apply(null, rgb);
      var min = Math.min.apply(null, rgb);
      var other = '';
      var maxIndex = rgb.indexOf(max);
      var minIndex = rgb.indexOf(min);
      if(max==min){
        maxIndex =0,minIndex =1;
        other = max;
      }else{
        other = rgb.filter(function(ele,index){
          return index!==maxIndex&&index!==minIndex;
        });
      }
      var rangeVal = '';
      switch([maxIndex,minIndex].toString()){
        case '0,1':
          rangeVal = rgb[2]*255/max;
          this.setState({rgba:[255,0,other,255]});
          break;
        case '2,1':
          rangeVal = 2*255-rgb[0]*255/max;
          this.setState({rgba:[other,0,max,255]});
          break;
        case '2,0':
          rangeVal = 2*255+rgb[1]*255/max;
          this.setState({rgba:[0,other,max,255]});
          break;
        case '1,0':
          rangeVal = 3*255-rgb[2]*255/max;
          this.setState({rgba:[0,255,other,255]});
          break;
        case '1,2':
          rangeVal = 3*255+rgb[0]*255/max;
          this.setState({rgba:[other,0,255,255]});
          break;
        case '0,2':
          rangeVal = 4*255-rgb[1]*255/max;
          this.setState({rgba:[max,other,0,255]});
          break;
      }
      renderBlockColor(this.state.rgba);
      $('.rgb-range').val(rangeVal);
      this.setState({value:rangeVal,y:parseInt(179-179*max/255),x:parseInt(359-359*min/max),pickRgb:rgb,
        hsl:rgbToHsl(rgb[0],rgb[1],rgb[2])});
    },
    handleRgbChange: function(event){
      var inputVal = $.trim(event.target.value);
      var index = $(event.target).parent().find('input').index($(event.target));
      var rgb = this.state.pickRgb;
      if(inputVal==''){return rgb[index]='',this.setState({pickRgb:rgb})}
      if(isNaN(inputVal)){return rgb[index]=0,this.setState({pickRgb:rgb})}

      var pattern = new RegExp('^[0-9]{0,3}');
      if(!pattern.test(inputVal)){
        return rgb[index]='',this.setState({pickRgb:rgb});
      }
      inputVal = parseInt(inputVal);
      if(inputVal>254){return false;}
      rgb[index] = inputVal;
      console.log(rgb);
      this.setState({pickRgb:rgb});
      var max = Math.max.apply(null, rgb);
      var min = Math.min.apply(null, rgb);
      var other = '';
      var maxIndex = rgb.indexOf(max);
      var minIndex = rgb.indexOf(min);
      if(max==min){
        maxIndex =0,minIndex =1;
        other = max;
      }else{
        other = rgb.filter(function(ele,index){
          return index!==maxIndex&&index!==minIndex;
        });
      }
      console.log([maxIndex,minIndex]);
      var rangeVal = '';
      switch([maxIndex,minIndex].toString()){
        case '0,1':
          rangeVal = rgb[2]*255/max;
          this.setState({rgba:[255,0,other,255]});
          break;
        case '2,1':
          rangeVal = 2*255-rgb[0]*255/max;
          this.setState({rgba:[other,0,max,255]});
          break;
        case '2,0':
          rangeVal = 2*255+rgb[1]*255/max;
          this.setState({rgba:[0,other,max,255]});
          break;
        case '1,0':
          rangeVal = 3*255-rgb[2]*255/max;
          this.setState({rgba:[0,255,other,255]});
          break;
        case '1,2':
          rangeVal = 3*255+rgb[0]*255/max;
          this.setState({rgba:[other,0,255,255]});
          break;
        case '0,2':
          rangeVal = 4*255-rgb[1]*255/max;
          this.setState({rgba:[max,other,0,255]});
          break;
      }
      renderBlockColor(this.state.rgba);
      $('.rgb-range').val(rangeVal);

      this.setState({value:rangeVal,y:parseInt(179-179*max/255),x:parseInt(359-359*min/max),pickRgb:rgb,
      hex:'#'+rgb.map(function(cv){
        if(cv>=16){
          return cv.toString(16);
        }else{
          return '0'+cv.toString(16);
        }
      }).join(''),hsl:rgbToHsl(rgb[0],rgb[1],rgb[2])});
    },

    handleAChange: function(event){
      var inputVal = $.trim(event.target.value);
      if(inputVal==''){return this.setState({a:''})}

      if(inputVal>=0||inputVal<=255){
        var rgba = this.state.rgba;
        var alpha = parseInt(inputVal);
        rgba[3] = alpha;
        this.setState({alpha:alpha,rgba:rgba});
        renderBlockColor(this.state.rgba);
      }
      this.setState({a:inputVal});
    },
    handleHslChange: function(event){
      var inputVal = $.trim(event.target.value);
      var hsl = this.state.hsl;
      var index = $(event.target).parent().find('input').index($(event.target));
      hsl[index] = inputVal;
      this.setState({hsl:hsl});
      var rgb = hslToRgb(parseInt(hsl[0])/360,parseInt(hsl[1].replace("%",""))/100,parseInt(hsl[2].replace("%",""))/100);
      var max = Math.max.apply(null, rgb);
      var min = Math.min.apply(null, rgb);
      var other = '';
      var maxIndex = rgb.indexOf(max);
      var minIndex = rgb.indexOf(min);
      if(max==min){
        maxIndex =0,minIndex =1;
        other = max;
      }else{
        other = rgb.filter(function(ele,index){
          return index!==maxIndex&&index!==minIndex;
        });
      }
      console.log([maxIndex,minIndex]);
      var rangeVal = '';
      switch([maxIndex,minIndex].toString()){
        case '0,1':
          rangeVal = rgb[2]*255/max;
          this.setState({rgba:[255,0,other,255]});
          break;
        case '2,1':
          rangeVal = 2*255-rgb[0]*255/max;
          this.setState({rgba:[other,0,max,255]});
          break;
        case '2,0':
          rangeVal = 2*255+rgb[1]*255/max;
          this.setState({rgba:[0,other,max,255]});
          break;
        case '1,0':
          rangeVal = 3*255-rgb[2]*255/max;
          this.setState({rgba:[0,255,other,255]});
          break;
        case '1,2':
          rangeVal = 3*255+rgb[0]*255/max;
          this.setState({rgba:[other,0,255,255]});
          break;
        case '0,2':
          rangeVal = 4*255-rgb[1]*255/max;
          this.setState({rgba:[max,other,0,255]});
          break;
      }
      renderBlockColor(this.state.rgba);
      $('.rgb-range').val(rangeVal);

      this.setState({value:rangeVal,y:parseInt(179-179*max/255),x:parseInt(359-359*min/max),pickRgb:rgb,hex:'#'+rgb.map(function(cv){
        if(cv>=16){
          return cv.toString(16);
        }else{
          return '0'+cv.toString(16);
        }
      }).join('')
      });
    },
    componentDidMount: function() {
      renderBlockColor(this.state.rgba);
    },
    render: function () {
      var value = this.state.value;
      var alpha = this.state.alpha;
      var x = this.state.x;
      var y = this.state.y;
      var hex = this.state.hex;
      var pickRgb = this.state.pickRgb;
      var hsl = this.state.hsl;
      return <div className="color-wrap">
        <canvas className="color-block"></canvas>
        <div className="color-picker-wrap" onClick = {this.handlePickerWrapClick}>
          <div className="color-picker" style={{left:x-6,top:y-6}}></div>
        </div>
        <div className="color-switch">
          <svg className="icon"  viewBox="0 0 1024 1024" width="20" height="20" fill={hex}>
            <path d="M825.196613 278.276576c15.172154-15.172154 15.172154-39.736595 0-54.908749L798.344309 196.515522c-15.172154-15.172154-39.736595-15.172154-54.908749 0l-65.866416 65.866416 81.761054 81.761054L825.196613 278.276576z">
            </path>
            <path d="M831.337723 416.752587l-226.378175-226.378175c-11.800564-11.800564-30.946378-11.800564-42.746943 0l-79.954845 79.954845c-11.800564 11.800564-11.800564 30.946378 0 42.746943l46.35936 46.35936L201.452493 686.479774c-20.109125 20.109125-20.109125 52.620884 0 72.730009l67.672625 67.672625c20.109125 20.109125 52.620884 20.109125 72.730009 0l327.164628-327.164628 39.495767 39.495767c11.800564 11.800564 30.946378 11.800564 42.746943 0l79.954845-79.954845C843.138288 447.578551 843.138288 428.553151 831.337723 416.752587zM333.185325 804.605833c-15.533396 15.533396-40.699906 15.533396-56.233302 0l-52.982126-52.982126c-15.533396-15.533396-15.533396-40.699906 0-56.233302L544.270931 374.968956l109.215428 109.215428L333.185325 804.605833z">
            </path>
            <path d="M201.813735 803.040452c0 13.125118 10.596425 23.721543 23.721543 23.721543l42.506115 0-66.227658-66.227658L201.813735 803.040452z">
            </path>
          </svg>
          <div className="circle" style={{background: hex,opacity:alpha/255}}></div>
        </div>
        <input className="rgb-range" type="range" min="0" step="1" max="1535" value={value} onChange={this.handleValueChange} />
        <input className="alpha-range" type="range" min="0" step="1" max="255" value={alpha} onChange={this.handleAlphaChange} />
        <ul className="color-show">
          <li className="hex">
            <input type="text" value={hex} onChange={this.handleHexChange} />
            <p>HEX</p>
          </li>
          <li className="rgba hide">
            <input type="text" value={pickRgb[0]} onChange={this.handleRgbChange} />
            <input type="text" value={pickRgb[1]} onChange={this.handleRgbChange} />
            <input type="text" value={pickRgb[2]} onChange={this.handleRgbChange} />
            <input type="text" value={alpha} onChange={this.handleAChange} />
            <p><span>R</span><span>G</span><span>B</span><span>A</span></p>
          </li>
          <li className="hsla hide">
            <input type="text" value={hsl[0]} onChange={this.handleHslChange} />
            <input type="text" value={hsl[1]} onChange={this.handleHslChange} />
            <input type="text" value={hsl[2]} onChange={this.handleHslChange} />
            <input type="text" value={alpha} onChange={this.handleAChange} />
            <p><span>H</span><span>S</span><span>L</span><span>A</span></p>
          </li>
        </ul>
        <svg className="switch" viewBox="0 0 1024 1024"  width="24" height="24" fill="#515151" onClick={this.handleSwitch}>
          <path d="M512 859.615l-260.711-260.711h521.422z"></path><path d="M512 164.385l-260.711 260.711h521.422z"></path>
        </svg>
      </div>;
    }
  });
  ReactDOM.render(<ColorWrap></ColorWrap>, document.querySelector('.container'));
</script>
</body>
</html>